#include "Core/Mesh.h"
#include "Core/MediaManager.h"
#include "Core/VBORenderBox.h"
#include "Core/Renderer.h"
#include "Debug/Logger.h"

namespace blue {

Mesh::Mesh(Model *model) :
  _textureT(NULL), _normalT(NULL), _model(model), _angle(0), _pos(Vector3d(0, 0, 0)), _scale(Vector3d(1, 1, 1)), _axes(Vector3d(0, 0, 1)), rdrbox(NULL), _multiTex_ext(false)
{
    if(_model) create();
}

Mesh::Mesh(const char *filename) :
  _textureT(NULL), _normalT(NULL), _model(NULL), _angle(0), _pos(Vector3d(0, 0, 0)), _scale(Vector3d(1, 1, 1)), _axes(Vector3d(0, 0, 1)), rdrbox(NULL), _multiTex_ext(false)

{
    _model = MediaManager::instance()->getMedia<Model>(filename);
    create();
}

Mesh::Mesh(IRenderBox *rdrbox) :
_textureT(NULL), _normalT(NULL), _model(NULL), _scale(Vector3d(1, 1, 1)), rdrbox(rdrbox), _multiTex_ext(false)

{
  _model = NULL;
}

Mesh::~Mesh()
{
    if(_textureT) _textureT->release();
    if(_normalT) _normalT->release();
    if(_model) _model->release();
    if(rdrbox) delete rdrbox;
}

void Mesh::load(const char *filename, IRenderBox *rdrbox)
{
    if(rdrbox) delete rdrbox;
    rdrbox = rdrbox;
    if(_textureT) _textureT->release();
    if(_normalT) _normalT->release();
    if(_model)
    {
        _model->release();
        _model = NULL;
    }
    _model = MediaManager::instance()->getMedia<Model>(filename);
    create();
}

void Mesh::setTexture(Texture *texture, texture_t type)
{
    switch(type)
    {
        case TEXTURE_TEXTURE: if(_textureT) _textureT->release(); _textureT = texture; break;
        case NORMAL_TEXTURE: if(_normalT) _normalT->release(); _normalT = texture; break;
        default: Logger::log() << "texture_type unknown : trying to add add a texture." << "\n";
    }
}

void Mesh::setTexture(const char *filename, texture_t type)
{
    switch(type)
    {
        case TEXTURE_TEXTURE: if(_textureT) _textureT->release(); _textureT = MediaManager::instance()->getMedia<Texture>(filename); break;
        case NORMAL_TEXTURE: if(_normalT) _normalT->release(); _normalT = MediaManager::instance()->getMedia<Texture>(filename); break;
        default: Logger::log() << "texture_type unknown : trying to add add a texture." << "\n";
    }
}

void Mesh::setPosition(const Vector3d &pos)
{
    _pos = pos;
}

void Mesh::setScale(const Vector3d &scale)
{
    _scale = scale;
}

void Mesh::setRotation(double angle, const Vector3d &axes)
{
  _angle = angle;
  _axes = axes;
}

const Vector3d &Mesh::getPosition()
{
    return _pos;
}

const Vector3d &Mesh::getScale()
{
    return _scale;
}

void Mesh::draw()
{
    if(rdrbox)
    {
        Renderer *rdr = Renderer::instance();

        rdr->pushMatrix();

        if(_multiTex_ext)
        {
            if(_textureT) rdr->setMultiTexture(_textureT, 0);
            if(_normalT) rdr->setMultiTexture(_normalT, 1);
        }
        else if(_textureT) rdr->setTexture(_textureT);

        rdr->scale(_scale);
        rdr->translate(_pos);
        rdr->rotate(_angle, _axes);

        rdrbox->draw();

        if(_multiTex_ext)
        {
            if(_textureT) rdr->setMultiTexture(NULL, 0);
            if(_normalT) rdr->setMultiTexture(NULL, 1);
        }
        else if(_textureT) rdr->setTexture(NULL);

        rdr->popMatrix();
    }
}

void Mesh::create()
{
    if(Renderer::instance()->checkExtension("GL_ARB_multitexture")) _multiTex_ext = true;
    else _multiTex_ext = false;
    if(!rdrbox)
    {
        if(Renderer::instance()->checkExtension("GL_ARB_vertex_buffer_object"))
            rdrbox = new VBORenderBox();
    }
    if(rdrbox)
    {
      rdrbox->initialize(GL_TRIANGLES);
      rdrbox->addBuffer(GL_VERTEX_ARRAY, _model->getBuffer(DATAF_POSITION).data, _model->getBuffer(DATAF_POSITION).size*sizeof(float), 3);
      rdrbox->addBuffer(GL_TEXTURE_COORD_ARRAY, _model->getBuffer(DATAF_TEXTURE).data, _model->getBuffer(DATAF_TEXTURE).size*sizeof(float), 2);
      rdrbox->addBuffer(GL_NORMAL_ARRAY, _model->getBuffer(DATAF_NORMAL).data, _model->getBuffer(DATAF_NORMAL).size*sizeof(float), 3);
      rdrbox->addBuffer(GL_INDEX_ARRAY, _model->getIndexBuffer().data, _model->getIndexBuffer().size*sizeof(int), 1);
    }
    else
        Logger::log() << "Mesh : " << _model->getName() << " initialized with no RenderBox !" << "\n";
}

}
